package ru.amse.tsyganov.jumleditor.view.lines;

import java.util.ArrayList;
import java.util.List;

import ru.amse.tsyganov.jumleditor.view.ConnectionPoint;

public class LineBuilder {

	public static Line[] getBrokenLine(ConnectionPoint source, ConnectionPoint target, 
			int v, int h) {
		
		switch (source.getType()) {
		case RIGHT:
		case LEFT:
			switch (target.getType()) {
			case RIGHT:
			case LEFT:
				return getThreeLines(source, target, true, v);
			case BOTTOM:
			case TOP:
				return getFourLines(source, target, true, v, h);	
			}
			break;
		case BOTTOM:
		case TOP:
			switch (target.getType()) {
			case RIGHT:
			case LEFT:
				return getFourLines(source, target, false, h, v);	
			case BOTTOM:
			case TOP:
				return getThreeLines(source, target, false, h);
			}
			break;
		}
		
		assert false;		
		return null;
	}

	public static Line[] getBrokenLine(ConnectionPoint source, ConnectionPoint target) {
		
		if (source == target) {
			return getClosureLine(source);
		}
		int d = 20;
		int v;
		int h;
		switch (source.getType()) {
		case RIGHT:
			switch (target.getType()) {
			case RIGHT:
				v = source.getCpX() > target.getCpX() ?
					source.getCpX() + d : target.getCpX() + d;
				
				return getThreeLines(source, target, true, v);
			case LEFT:
				if (source.getCpY() == target.getCpY()) {
					return getOneLine(source, target);
				}
				v = source.getCpX() > target.getCpX() ?
					source.getCpX() + d : (source.getCpX() + target.getCpX()) / 2;
				return getThreeLines(source, target, true, v);
			case BOTTOM:
				if (source.getCpX() < target.getCpX() && source.getCpY() > target.getCpY()) {
					return getTwoLines(source, target, true);
				}
				h = source.getCpY() > target.getCpY() ?
					(source.getCpY() + target.getCpY()) / 2 : target.getCpY() + d;
				v = source.getCpX() > target.getCpX() ?
					source.getCpX() + d : (source.getCpX() + target.getCpX()) / 2;
				return getFourLines(source, target, true, v, h);
			case TOP:
				if (source.getCpX() < target.getCpX() && source.getCpY() < target.getCpY()) {
					return getTwoLines(source, target, true);
				}
				h = source.getCpY() < target.getCpY() ?
					(source.getCpY() + target.getCpY()) / 2 : target.getCpY() - d;
				v = source.getCpX() > target.getCpX() ?
					source.getCpX() + d : (source.getCpX() + target.getCpX()) / 2;
				return getFourLines(source, target, true, v, h);	
			}
			break;
		case LEFT:
			switch (target.getType()) {
			case RIGHT:
				if (source.getCpY() == target.getCpY()) {
					return getOneLine(source, target);
				}
				v = source.getCpX() > target.getCpX() ?
					source.getCpX() - d : (source.getCpX() + target.getCpX()) / 2;
				
				return getThreeLines(source, target, true, v);	
			case LEFT:
				v = source.getCpX() < target.getCpX() ?
					source.getCpX() - d : target.getCpX() - d;
					
				return getThreeLines(source, target, true, v);
			case BOTTOM:
				if (source.getCpX() > target.getCpX() && source.getCpY() > target.getCpY()) {
					return getTwoLines(source, target, true);
				}
				h = source.getCpY() > target.getCpY() ?
					(source.getCpY() + target.getCpY()) / 2 : target.getCpY() + d;
				v = source.getCpX() < target.getCpX() ?
					source.getCpX() - d : (source.getCpX() + target.getCpX()) / 2;
				
				return getFourLines(source, target, true, v, h);
			case TOP:
				if (source.getCpX() > target.getCpX() && source.getCpY() < target.getCpY()) {
					return getTwoLines(source, target, true);
				}
				h = source.getCpY() < target.getCpY() ?
					(source.getCpY() + target.getCpY()) / 2 : target.getCpY() - d;
				v = source.getCpX() < target.getCpX() ?
					source.getCpX() - d : (source.getCpX() + target.getCpX()) / 2;
				
				return getFourLines(source, target, true, v, h);	
			}
			break;
		case BOTTOM:
			switch (target.getType()) {
			case RIGHT:
				if (source.getCpX() > target.getCpX() && source.getCpY() < target.getCpY()) {
					return getTwoLines(source, target, false);
				}
				h = target.getCpY() > source.getCpY() ?
					(target.getCpY() + source.getCpY()) / 2 : source.getCpY() + d;
				v = target.getCpX() > source.getCpX() ?
					target.getCpX() + d : (target.getCpX() + source.getCpX()) / 2;
				
				return getFourLines(source, target, false, h, v);
			case LEFT:
				if (source.getCpX() < target.getCpX() && source.getCpY() < target.getCpY()) {
					return getTwoLines(source, target, false);
				}
				h = target.getCpY() > source.getCpY() ?
					(target.getCpY() + source.getCpY()) / 2 : source.getCpY() + d;
				v = target.getCpX() < source.getCpX() ?
					target.getCpX() - d : (target.getCpX() + source.getCpX()) / 2;
				
				return getFourLines(source, target, false, h, v);	
			case BOTTOM:
				h = source.getCpY() > target.getCpY() ?
					source.getCpY() + d : target.getCpY() + d;
				
				return getThreeLines(source, target, false, h);
			case TOP:
				if (source.getCpX() == target.getCpX()) {
					return getOneLine(source, target);
				}
				h = source.getCpY() > target.getCpY() ?
					source.getCpY() + d : (source.getCpY() + target.getCpY()) / 2;
				
				return getThreeLines(source, target, false, h);
			}
			break;
		case TOP:
			switch (target.getType()) {
			case RIGHT:
				if (source.getCpX() > target.getCpX() && source.getCpY() > target.getCpY()) {
					return getTwoLines(source, target, false);
				}
				h = target.getCpY() < source.getCpY() ?
					(target.getCpY() + source.getCpY()) / 2 : source.getCpY() - d;
				v = target.getCpX() > source.getCpX() ?
					target.getCpX() + d : (target.getCpX() + source.getCpX()) / 2;
				
				return getFourLines(source, target, false, h, v);	
			case LEFT:
				if (source.getCpX() < target.getCpX() && source.getCpY() > target.getCpY()) {
					return getTwoLines(source, target, false);
				}
				h = target.getCpY() < source.getCpY() ?
					(target.getCpY() + source.getCpY()) / 2 : source.getCpY() - d;
				v = target.getCpX() < source.getCpX() ?
					target.getCpX() - d : (target.getCpX() + source.getCpX()) / 2;
				
				return getFourLines(source, target, false, h, v);	
			case BOTTOM:
				if (source.getCpX() == target.getCpX()) {
					return getOneLine(source, target);
				}
				h = target.getCpY() > source.getCpY() ?
					source.getCpY() - d : (target.getCpY() + source.getCpY()) / 2;
				
				return getThreeLines(source, target, false, h);
			case TOP:
				h = source.getCpY() < target.getCpY() ?
					source.getCpY() - d : target.getCpY() - d;
					
				return getThreeLines(source, target, false, h);
			}
			break;
		}
		
		assert false;		
		return null;
	}

	public static Line[] getFourLines(ConnectionPoint source, ConnectionPoint target,
			boolean firstHorizontal,
			int coord1, int coord2) {
		
		List<Line> lines = new ArrayList<Line>();
		
		Line fromPoint = FromConnectionPointLine.
			createNewFromConnectionPointLine(source, firstHorizontal, coord1);
		Line toPoint = ToConnectionPointLine.createNewToConnectionPointLine(
				target, !firstHorizontal, coord2);
		
		MiddleLine middleLine1 = MiddleLine.createNewMiddleLine(!firstHorizontal);
		MiddleLine middleLine2 = MiddleLine.createNewMiddleLine(firstHorizontal);
		
		middleLine1.setFromLine(fromPoint);
		middleLine1.setToLine(middleLine2);
		
		middleLine2.setFromLine(middleLine1);
		middleLine2.setToLine(toPoint);
		
		lines.add(fromPoint);
		lines.add(middleLine1);
		lines.add(middleLine2);
		lines.add(toPoint);
		
		return lines.toArray(new Line[lines.size()]);
	}

	public static Line[] getThreeLines(ConnectionPoint source, ConnectionPoint target,
			boolean firstHorizontal,
			int coord1) {
		
		List<Line> lines = new ArrayList<Line>();
		
		Line fromPoint = FromConnectionPointLine.
			createNewFromConnectionPointLine(source, firstHorizontal, coord1);
		Line toPoint = ToConnectionPointLine.createNewToConnectionPointLine(
				target, firstHorizontal, coord1);
		
		MiddleLine middleLine1 = MiddleLine.createNewMiddleLine(!firstHorizontal);
		
		middleLine1.setFromLine(fromPoint);
		middleLine1.setToLine(toPoint);
		
		lines.add(fromPoint);
		lines.add(middleLine1);
		lines.add(toPoint);
		
		return lines.toArray(new Line[lines.size()]);
	}

	public static Line[] getTwoLines(ConnectionPoint source, ConnectionPoint target,
			boolean firstHorizontal) {
		
		List<Line> lines = new ArrayList<Line>();
		
		Line fromPoint = FromConnectionPointLine.createNewFromConnectionPointLine(
				source, firstHorizontal, 
				firstHorizontal ? target.getCpX() : target.getCpY());
		Line toPoint = ToConnectionPointLine.createNewToConnectionPointLine(
				target, !firstHorizontal, 
				firstHorizontal ? source.getCpY() : source.getCpX());
		
		lines.add(fromPoint);
		lines.add(toPoint);
		
		return lines.toArray(new Line[lines.size()]);
	}

	public static Line[] getOneLine(ConnectionPoint source, ConnectionPoint target) {
		return new Line[]{ 
				new SimpleLine(
						source.getCpX(), source.getCpY(), 
						target.getCpX(), target.getCpY()) };
	}

	private static Line[] getClosureLine(ConnectionPoint p) {
		int d = 80;
		int r = 25;
		Line first = null;
		Line last = null;
		MiddleLine middle = null;
		switch (p.getType()) {
		case BOTTOM: 
			first = new SimpleLine(p.getCpX(), p.getCpY(), p.getCpX(), p.getCpY() + d);
			last = new SimpleLine(p.getCpX() + r, p.getCpY() + d, p.getCpX() + r, p.getCpY());
			middle = new HorizontalMiddleLine();
			break;
		case TOP:
			first = new SimpleLine(p.getCpX(), p.getCpY(), p.getCpX(), p.getCpY() - d);
			last = new SimpleLine(p.getCpX() - r, p.getCpY() - d, p.getCpX() - r, p.getCpY());
			middle = new HorizontalMiddleLine();
			break;
		case LEFT:
			first = new SimpleLine(p.getCpX(), p.getCpY(), p.getCpX() - d, p.getCpY());
			last = new SimpleLine(p.getCpX() - d, p.getCpY() + r, p.getCpX(), p.getCpY() + r);
			middle = new VerticalMiddleLine();
			break;
		case RIGHT:
			first = new SimpleLine(p.getCpX(), p.getCpY(), p.getCpX() + d, p.getCpY());
			last = new SimpleLine(p.getCpX() + d, p.getCpY() - r, p.getCpX(), p.getCpY() - r);
			middle = new VerticalMiddleLine();
			break;
		}
		
		middle.setFromLine(first);
		middle.setToLine(last);
		return new Line[] {first, middle, last};
	}
}
